## -*- coding: cp1252 -*-
import wx
import wx.aui
import wx.grid
import wx.lib.agw.genericmessagedialog as GMD

import scipy.stats as s
import numpy
import os
from math import sqrt, log
import win32com.client
from string import ascii_letters

import excel_chart

# outlier test on %, statistics on log2

# Delete all file content by start
app_path = "D:\\Eigene Datein\\Python\\TIGS DB\\"
source_path = app_path + "files\\"
image_path = app_path + "images\\"

f = open(source_path + 'statistic_data.txt', 'w')
f_result = open(source_path + 'statistic_result.txt', 'w')
f_outlier_id = open(source_path + 'outlier_id.txt', 'w')
f_result.close()
f.close()
f_outlier_id.close()

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        #appIcon = wx.Icon('help.png', wx.BITMAP_TYPE_PNG, 16, 16)
        #wx.Frame.SetIcon(self, appIcon)

        self.mgr = wx.aui.AuiManager(self)

        # 3 Panels
        self.leftpanel = wx.Panel(self, -1, size = (200, 150))
        self.rightpanel = wx.Panel(self, -1, size = (200, 150))
        self.bottompanel = wx.Panel(self, -1, size = (200, 150))
        self.rightpanel.SetFocus()
        #-----------------------------------------------------------------------
        # Datagrid right panel
        self.grid = wx.grid.Grid(self.rightpanel)
        self.grid.CreateGrid(50, 100)
        self.grid.EnableEditing(False)

        ### Widgets left panel
        # Median Box
        self.radio_median1 = wx.RadioButton(self.leftpanel, -1, "0" )
        self.radio_median2 = wx.RadioButton(self.leftpanel, -1, "100" )
        self.radio_median4 = wx.RadioButton(self.leftpanel, -1, "1" )
        self.radio_median3 = wx.RadioButton(self.leftpanel, -1, "Custom:" )
        self.radio_median3.SetValue(True)
        self.median = wx.TextCtrl(self.leftpanel, -1, "-0.299")
        # Sizer Median
        vs = wx.BoxSizer(wx.VERTICAL)
        box_title_median = wx.StaticBox(self.leftpanel, -1, "Hypothetical mean")
        box_median = wx.StaticBoxSizer(box_title_median, wx.VERTICAL)
        median_grid = wx.FlexGridSizer(0, 1, 0, 0 )
        self.group_median = []
        self.group_median.append((self.radio_median1, self.radio_median4, self.radio_median2, self.radio_median3, self.median))
        for item1, item2, item3, item4, item5 in self.group_median:
            median_grid.Add(item1, 0, wx.ALIGN_LEFT|wx.LEFT|wx.RIGHT|wx.TOP, 5)
            median_grid.Add(item2, 0, wx.ALIGN_LEFT|wx.LEFT|wx.RIGHT|wx.TOP, 5)
            median_grid.Add(item3, 0, wx.ALIGN_LEFT|wx.LEFT|wx.RIGHT|wx.TOP, 5)
            median_grid.Add(item4, 0, wx.ALIGN_LEFT|wx.LEFT|wx.RIGHT|wx.TOP, 5)
            median_grid.Add(item5, 0, wx.ALIGN_LEFT|wx.LEFT|wx.RIGHT|wx.TOP, 5)
        box_median.Add(median_grid, 0, wx.ALIGN_LEFT|wx.ALL)
        vs.Add(box_median, 0, wx.ALIGN_LEFT|wx.ALL)

        # Zero Box
        #self.zero_cb = wx.CheckBox(self.leftpanel, -1, "")
        #self.zero_cb.SetValue(True)
        self.zero = wx.TextCtrl(self.leftpanel, -1, "13")#, size=(73, -1))
        # Sizer Median
        vs = wx.BoxSizer(wx.VERTICAL)
        box_title_zero = wx.StaticBox(self.leftpanel, -1, "Replace Zero with")
        box_zero = wx.StaticBoxSizer(box_title_zero, wx.VERTICAL)
        zero_grid = wx.FlexGridSizer(0, 1, 0, 0 )
        self.group_zero = []
        self.group_zero.append((self.zero))
        for item1 in self.group_zero:
            zero_grid.Add(item1, 0, wx.ALIGN_CENTER|wx.LEFT|wx.RIGHT|wx.TOP, 5)
            #zero_grid.Add(item2, 0, wx.ALIGN_LEFT|wx.LEFT|wx.RIGHT|wx.TOP, 5)

        box_zero.Add(zero_grid, 0, wx.ALIGN_LEFT|wx.ALL)
        vs.Add(box_zero, 0, wx.ALIGN_LEFT|wx.ALL)

        self.calc = wx.Button(self.leftpanel, -1, "Calculate", size=(100,50))

        # Bottom Panel
        self.text_summary = wx.StaticText(self.bottompanel, -1, "Quick start")
        self.text_outlier = wx.StaticText(self.bottompanel, -1, "Paste data directly from Excel in the following format:")
        self.text_signif = wx.StaticText(self.bottompanel, -1, "IDX-Value1-Value2-Value3-Value4\nIDY-Value1-Value2-Value3\n...")

        self.text_summary.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL))
        self.text_outlier.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL))
        self.text_signif.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL))

        #-----------------------------------------------------------------------
        # Add panels
        self.mgr.AddPane(self.bottompanel, wx.aui.AuiPaneInfo().Bottom())
        self.mgr.AddPane(self.leftpanel, wx.aui.AuiPaneInfo().Left().Layer(1))
        self.mgr.AddPane(self.rightpanel, wx.aui.AuiPaneInfo().Center().Layer(2))

        #-----------------------------------------------------------------------
        # Layout
        # Right Panel
        sizer1 = wx.BoxSizer(wx.VERTICAL)
        sizer1.Add(self.grid, 1, wx.EXPAND)
        self.rightpanel.SetSizer(sizer1)

        # Left Panel
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        sizer = wx.GridBagSizer(hgap=5, vgap=5)
        sizer.Add(box_median, pos=(0, 0))
        sizer.Add(box_zero, pos=(2, 0), flag=wx.EXPAND)
        sizer.Add(self.calc, pos=(4, 0))
        mainSizer.Add(sizer, 0, wx.ALL, 20)
        self.leftpanel.SetSizer(mainSizer)

        # Bottom Panel
        mainSizer2 = wx.BoxSizer(wx.VERTICAL)
        sizer = wx.GridBagSizer(hgap=5, vgap=5)
        sizer.Add(self.text_summary, pos=(0, 0))
        sizer.Add(self.text_outlier, pos=(1, 0))
        sizer.Add(self.text_signif, pos=(2, 0))
        mainSizer2.Add(sizer, 0, wx.ALL, 20)
        self.bottompanel.SetSizer(mainSizer2)

        #-----------------------------------------------------------------------
        # Events
        self.rightpanel.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
        self.Bind(wx.EVT_BUTTON, self.calc_statistics, self.calc)
        #-----------------------------------------------------------------------
        self.Maximize()
        self.make_menue()
        self.mgr.Update()

        ### ONLY FOR TIGS DB!!!
        self.OnGetDataFromTIGSDB()



    def dict_laden(self, pfad):
        """Load the correct Nalimov value from a dict."""
        z_value ={}
        datei = file(pfad)
        liste = datei.readlines()
        for eintrag in liste:
            l_eintrag = eintrag.split('\t')
            z_value[int(l_eintrag[0])] = float(l_eintrag[2])
        datei.close()
        return z_value


    def calc_statistics(self, evt):
        """Calculate all the statistics per data set."""
        self.rightpanel.SetFocus()

        # Read the data from the grid.
        f = open(source_path + 'statistic_data.txt', 'r')
        data = f.readlines()
        f.close()

        # Zero Replacement
        self.zero_replace_value = float(self.zero.GetValue())
        if self.zero_replace_value == 0:
            unequal = u'\u2260'
            msg = "Zero replacement value can't be zero.\nPlease enter a valid value (" + unequal + " 0)."
            dlg = GMD.GenericMessageDialog(self, msg,
                       "Zero Division Error",
                       wx.OK|wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()

        else:
            # Control Median
            if self.radio_median1.GetValue() is True:
                control_median = float(0)
            elif self.radio_median4.GetValue() is True:
                control_median = float(1)
            elif self.radio_median2.GetValue() is True:
                control_median = float(100)
            elif self.radio_median3.GetValue() is True:
                control_median = float(self.median.GetValue())

            # Nr of constructs which.
            self.request_nr = len(data)
            self.max_values = self.max_values
            # Write column titles.
            # List for column labels of the grid.
            col_title_lst = []
            col_title_lst.append('ID')
            for max_value in range(self.max_values):
                col_title_lst.append('%')
            for max_value in range(self.max_values):
                col_title_lst.append('Log2')
            for max_value in range(self.max_values):
                col_title_lst.append('Nalimov')
            col_title_lst.extend(['Mean', 'STD', 'SEM', 'Ttest'])

            # Max  Nr column of the grid.
            self.max_columns = (self.max_values*3) + 5

            for column in range(self.max_columns):
                self.grid.SetColLabelValue(column, str(col_title_lst[column]))

            # String for result file.
            col_title = 'ID\t'
            col_title = col_title + ('%\t'*self.max_values)
            col_title = col_title + ('Log2\t'*self.max_values)
            col_title = col_title + ('Nalimov\t'*self.max_values)
            col_title = col_title + ('Mean\tSTD\tSEM\tTtest')

            # Open files, generate empty lists.
            f_result = open(source_path + 'statistic_result.txt', 'w')
            f_result.write(col_title + '\n')

            value_lst = []
            value_lst_result = []
            value_lst_log = []
            result_lst = []
            self.final_result_lst = []
            values_nr = 1
            self.significant = []
            self.outlier = []
            value_pos = 1

            # Split the data sets and append data to a list.
            for line in data:
                line = line.replace('\t\n', '\n')
                data_set = line.split('\t')
                data_name = data_set[0]
                data_set_values = data_set[1:]
                #max_values = len(data_set_values)

                for value in data_set_values:
                    value = value.replace(',', '.')
                    value = value.replace('\n', '')


                    # Values in %.
                    try:
                        if float(value) == 0:
                            value = self.zero_replace_value

                        value_lst.append(float(value))
                        value_lst_log.append(log(float(float(value)/100), 2))

                    except ValueError:
                        if value == '':
                            value_lst.append(None)
                            value_lst_log.append(None)

                    value_pos = value_pos + 1

                # Check for outliers, and get list of log2 transformed values.
                self.nalimov(value_lst, values_nr)

                # Calculate statistics on data set without outlier and log2 transformed.
                mean = numpy.mean(self.value_lst_wo_outlier)
                try:
                    std = s.tstd(self.value_lst_wo_outlier)
                except ZeroDivisionError:
                    std = 0
                sem = s.sem(self.value_lst_wo_outlier)

                # Calculate ttest, default is control median -0.299 (all first round shots).

                ttest = s.ttest_1samp(self.value_lst_wo_outlier, control_median)
                pvalue = float(ttest[1])
                if pvalue < 0.05:
                    self.significant.append(values_nr)


                if len(value_lst) or len(value_lst_log) or len(self.nalimov_values) != self.max_values:
                    dummy_value = ''
                    for i in range((self.max_values-len(value_lst))):
                        value_lst.append(dummy_value)
                        value_lst_log.append(dummy_value)
                        self.nalimov_values.append(dummy_value)

                result_lst.extend([data_name] + value_lst + value_lst_log + self.nalimov_values + [mean] +  [std] + [sem] + [pvalue])


                self.final_result_lst.append(result_lst)

                # Empty value list for next data set
                value_lst = []
                value_lst_log = []
                result_lst = []
                values_nr = values_nr + 1
                value_pos = 1

            # Put data from final_result_lst into the grid.
            for row in range(len(self.final_result_lst)):
                for col in range(len(self.final_result_lst[0])):
                    if self.final_result_lst[row][col] == None:
                        self.final_result_lst[row][col] = ''
                    try:
                        self.grid.SetCellValue(row, col, str(self.final_result_lst[row][col]))
                        f_result.write(str(self.final_result_lst[row][col]) + '\t')

                    except IndexError:
                        self.grid.SetCellValue(row, col, str("na"))
                f_result.write('\n')

            f_result.close()

            # Draw background colour, green if significant, red if outlier
            for signi in self.significant:
                self.grid.SetCellBackgroundColour(signi-1, self.max_columns-1, wx.GREEN)
            for outl in self.outlier:
                self.grid.SetCellBackgroundColour(outl[0]-1, outl[1]-1, wx.RED)

            self.make_summary()

    def make_summary(self):
        """Updates a summary to the bottompanel."""
        self.text_summary.SetLabel("Summary")
        self.text_outlier.SetLabel(str(len(self.outlier))+' Outlier(s) found.')
        self.text_signif.SetLabel(str(len(self.significant))+' Significant constructs.')

    def nalimov(self, value_lst, values_nr):
        """Perform a Nalimov test on a data set. See http://www.statistics4u.info/fundstat_germ/ee_nalimov_outliertest.html"""
        # Calculate mean and std
        mean = numpy.mean(filter(None,value_lst))
        #mean = numpy.mean(value_lst)
        try:
            std = s.tstd(filter(None,value_lst))
        except ZeroDivisionError:
            std = 0

        # Get Nalimov table
        z_value = self.dict_laden(source_path  + 'nalimov_table.txt')
        self.value_lst_wo_outlier = []
        self.nalimov_values = []

        # Outlier
        f_outlier_id = open(source_path  + 'outlier_id.txt', 'a')
        row = 2
        for value in value_lst:
            if value != None:
                n_values = float(len(value_lst))
                p = sqrt(n_values/(n_values-1.0))
                nalimov_value = (abs((value-mean))/std) * p

                # Get critical value
                out_val = z_value[len(value_lst)-2]

                # If critical value is >, then outlier
                if nalimov_value > out_val:
                    nalimov_value = str(nalimov_value)
                    f_outlier_id.write(str(values_nr+1) + '\t' + str(row) + '\n')
                    self.outlier.append((values_nr, row))

                else:
                    try:
                        self.value_lst_wo_outlier.append(log(float(float(value)/100), 2))
                    except ValueError:
                        pass

                row = row + 1
                self.nalimov_values.append(nalimov_value)
            else:
                self.nalimov_values.append(None)



    def OnKeyDown(self, evt):
        "Manage paste event str + v"
        key = evt.GetKeyCode()

        if key == ord('V') and evt.ControlDown(): # not CtrlDown()!
            if evt.ShiftDown() or evt.AltDown():
                self.OnPaste(evt)

            else:
                self.OnPaste(evt)

            evt.Skip()

        elif key == ord('C') and evt.ControlDown(): # not CtrlDown()!
            if evt.ShiftDown() or evt.AltDown():
                self.OnCopy()

            else:
                self.OnCopy()

            evt.Skip()

        elif key == 127:
            self.OnDelete(evt)

    def OnPaste(self, evt):
        " Paste data from clipboard to grid."
        if not wx.TheClipboard.IsOpened():
            do = wx.TextDataObject()
            wx.TheClipboard.Open()
            success = wx.TheClipboard.GetData(do)
            wx.TheClipboard.Clear()
            print 'no', do.GetText()

            wx.TheClipboard.Close()

            if success:
                data = do.GetText()
            else:
                pass

        f = open(source_path  + 'pasted_data.txt', 'w')
        f.write(data)

        f.close()
        f = open(source_path  + 'pasted_data.txt', 'r')
        hits = f.readlines()
        f.close()
        data = []

        # Max Nr of values in self.request_nr.
        max_values_lst = []
        for i in hits:
            x = i.split('\t')
            max_value = len(x) - 1
            max_values_lst.append(max_value)
            data.append(x)

        self.max_values = max(max_values_lst)

        for row in range(len(data)):
            for col in range(self.max_values+1):
                try:
                    self.grid.SetCellValue(row, col, str(data[row][col]))
                except IndexError:
                    pass
                    #print str(data[row][col])

    def OnCopy(self):
        """Copy data from grid to clipboard."""
        f = open(source_path  + 'statistic_result.txt', 'r')
        results = f.read()
        results = results.replace('.', ',')
        f.close()

        data = wx.TextDataObject()
        data.SetText(results)
        if not wx.TheClipboard.IsOpened():
            wx.TheClipboard.Open()
            wx.TheClipboard.SetData(data)

    def OnDelete(self, evt):
        """Clear all the data from the grid."""
        self.grid.ClearGrid()
        for signi in self.significant:
            self.grid.SetCellBackgroundColour(signi-1, self.max_columns-1, wx.WHITE)
        for outl in self.outlier:
            self.grid.SetCellBackgroundColour(outl[0]-1, outl[1]-1, wx.WHITE)

    def OnGetDataFromTIGSDB(self):
        """Only in combination with TIGS DB!. Takes final data
        from TIGS DB request and calulate statistics."""
        f = open(source_path  + 'statistic_data.txt', 'r')
        hits = f.readlines()

        f.close()
        data = []

        # Max Nr of values in self.request_nr.
        max_values_lst = []
        for i in hits:
            i = i.replace('\t\n', '\n')
            x = i.split('\t')
            max_value = len(x) - 1
            max_values_lst.append(max_value)
            data.append(x)

        #print data, len(data)
        self.max_values = max(max_values_lst)

        for row in range(len(data)):
            for col in range(self.max_values + 1):
                try:

                    self.grid.SetCellValue(row, col, str(data[row][col]))
                except IndexError:
                    pass
                    #print str(data[row][col])



    def create_excel(self, evt):
        """Creates a Excel file with diagram."""
        # Get results.
        f_out = open(source_path  + 'statistic_result.txt', 'r')
        f_out.close()
        f_out = open(source_path  + 'statistic_result.txt', 'r')

        # Variables for the excel file.
        errorbar_column = (self.max_values*3) + 3
        errorbar_row_start = 2
        errorbar_row_end = self.request_nr + 1

        row_start = 2
        row_end = self.request_nr + 1
        label_column = 1
        value_col = (self.max_values*3) + 2

        data = f_out.readlines()
        values = []
        for i in data:
            i = i.strip()
            i = i.split('\t')
            values.append(i)

        # Start Excel.
        excel = excel_chart.ExcelWorkbook()

        # Add new worksheet.
        sheet = excel.newSheet('Statistic')

        # Add the data, starting from A1.
        excel.setRange('Statistic', 1, 1, values)

        # Create a Barchart with errorbars.
        chart = excel.newChartInSheet('Statistic', top=15*self.request_nr)
        errorbars = {'amount':(errorbar_row_start, errorbar_row_end, errorbar_column)}
        excel.addXYChartSeries('Statistic', row_start, row_end, label_column, value_col, chart_sheet=chart, errorbars=errorbars, title = 'TIGS Statistic',
                           ylabel='Mean effect log2', xlabel='IDs')

        # Draw all outliers red.
        excel.setOutliers('Statistic')
        # Set Number format % 2 digits
        excel.setNumberFormat('Statistic', 2, 2, self.request_nr+1, self.max_values+1, "0,00")
        excel.setNumberFormat('Statistic', 2, self.max_values+2, self.request_nr+1, (self.max_values*3)+5, "0,000")

        # Save the file.
        #excel.save('final.xls')
        excel.show()
        #excel.close()


    def make_menue(self):
        # Toolbar
        toolBar = self.CreateToolBar()
        toolBar.AddLabelTool(30, '',
                             wx.Bitmap(image_path + 'paste.png'), shortHelp="Paste data")
        toolBar.AddLabelTool(20, '',
                             wx.Bitmap(image_path + 'clear.png'), shortHelp="Clear data")
        toolBar.AddSeparator()
        toolBar.AddLabelTool(40, '',
                             wx.Bitmap(image_path + 'kcalc.png'), shortHelp="Start Calculation")
        toolBar.AddSeparator()
        toolBar.AddLabelTool(10, '',
                             wx.Bitmap(image_path + 'Excel.png'), shortHelp="Export to Excel")
        toolBar.AddSeparator()

        toolBar.AddLabelTool(50, '',
                             wx.Bitmap(image_path + 'conf.png'), shortHelp="Settings")
        toolBar.AddLabelTool(wx.ID_ANY, '',
                             wx.Bitmap(image_path + 'help.png'), shortHelp="Help")

        # Events
        self.Bind(wx.EVT_TOOL, self.create_excel, id=10)
        self.Bind(wx.EVT_TOOL, self.OnDelete, id=20)
        self.Bind(wx.EVT_TOOL, self.OnPaste, id=30)
        self.Bind(wx.EVT_TOOL, self.calc_statistics, id=40)
        self.Bind(wx.EVT_TOOL, self.pop_menu, id=50)

        toolBar.Realize()


    def pop_menu(self, evt):
        """Pop up menu for settings."""
        menu = wx.Menu()
        sm = wx.Menu()
        self.decimal_point = sm.Append(4, "Dezimal Charakter Point", kind=wx.ITEM_CHECK)
        self.decimal_comma = sm.Append(5, "Dezimal Charakter Comma", kind=wx.ITEM_CHECK)
        sm.Check(4, True)
        self.Bind(wx.EVT_MENU, self.excel_setting, id=4)
        self.Bind(wx.EVT_MENU, self.excel_setting, id=5)
        menu.AppendMenu(6, "Excel Settings", sm)
        self.PopupMenu(menu, (244,50))
        menu.Destroy()

    def excel_setting(self):
        print self.decimal_point.IsChecked()
        print self.decimal_comma.IsChecked()

class MyApp(wx.App):
    def OnInit(self):
        frame = MyFrame(None, -1, 'sTAtistic.py')
        frame.Show()
        self.SetTopWindow(frame)
        return 1

if __name__ == "__main__":
    app = MyApp(0)
    app.MainLoop()